#include <linux/module.h>
#include <linux/pci.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/msi.h>
#include <linux/mutex.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include "drv_pi.h"

#define PI_MODE_MASK            0x001C0000      /* PI 模式掩码 */
#define PI_EDGE_MASK            0x00030000      /* PI 边沿掩码 */
#define PI_DELAY_MASK           0x0000FFFF      /* PI 延时掩码 */
#define PI_MODE_SHIFT           18              /* PI 模式偏移 */
#define PI_EDGE_SHIFT           16              /* PI 边沿偏移 */
#define PI_DELAY_SHIFT          0               /* PI 延时偏移 */
#define PI_DEF_MODE             PI_MOD_LEVEL    /* PI 默认模式 */
#define PI_DEF_EDGE             PI_EDGE_RISE    /* PI 默认边沿 */
#define PI_DEF_DELAY            10              /* PI 默认延时, 0~65535us */

#define PI_MSI_MASK             0x01            /* PI MSI 掩码 */

#define PI_CYCLE_REG_LSB        8               /* PI 周期寄存器最小计数单位，单位：ns */
#define PI_MAX_CYCLE            0xFFFFFFFF      /* PI 最大周期值 */

#define PI_AC_CH0               16              /* PI 在 AC 模块的通道0 */
#define PI_AC_CH1               17              /* PI 在 AC 模块的通道1 */

/* PI 寄存器*/
enum di_reg_t {
    PI_VERSION_OFFSET           = 0x04,         /* 版本 寄存器 */
    PI_MODE_OFFSET              = 0x6C,         /* PI 模式寄存器偏移 */
    PI_EDGE_ID_OFFSET           = 0x74,         /* PI 边沿通道寄存器偏移 */
    PI_CYCLE_OFFSET             = 0x98,         /* PI 周期寄存器偏移 */
    PI_AC_CYCLE1_OFFSET         = 0x9C,         /* AC 周期1寄存器偏移 */
    PI_AC_CYCLE2_OFFSET         = 0xA0,         /* AC 周期2寄存器偏移 */
    PI_ID_OFFSET                = 0xA4,         /* PI 通道号寄存器偏移 */
};

/* 文件操作函数声明 */
static int open_pi(struct inode *inode, struct file *filp);
static int release_pi(struct inode *inode, struct file *filp);
static ssize_t read_pi(struct file *filp, char __user *buf, 
                       size_t count, loff_t *ppos);
static long ioctl_pi(struct file *filp, unsigned int cmd, 
                     unsigned long arg);
/* 文件操作结构体 */
static const struct file_operations cdev_fops = {
    .owner = THIS_MODULE,
    .open = open_pi,
    .release = release_pi,
    .read = read_pi,
    .write = NULL,
    .unlocked_ioctl = ioctl_pi,
    .poll = NULL,
    .llseek = no_llseek,
};

/* 读取 PI 寄存器 */
static u32 read_pi_reg_le(struct pcie_dev_t *pcie_dev, u32 reg)
{
    u32 val = 0;
    unsigned long flags = 0;    /* 用于保存中断状态 */

    /* 获取自旋锁(保存中断状态) */
    spin_lock_irqsave(&pcie_dev->splock, flags);
    val = ioread32(pcie_dev->bar[0] + reg);
    /* 释放自旋锁(恢复中断状态) */
    spin_unlock_irqrestore(&pcie_dev->splock, flags);
    pr_debug("%s  reg=0x%08x, val=0x%08x", __func__, reg, val);

    return val;
}

/* 写入 PI 寄存器 */
static void write_pi_reg_le(struct pcie_dev_t *pcie_dev, u32 reg, u32 val)
{
    unsigned long flags = 0;    /* 用于保存中断状态 */

    /* 获取自旋锁(保存中断状态) */
    spin_lock_irqsave(&pcie_dev->splock, flags);
    iowrite32(val, pcie_dev->bar[0] + reg);
    /* 释放自旋锁(恢复中断状态) */
    spin_unlock_irqrestore(&pcie_dev->splock, flags);
    pr_debug("%s reg=0x%08x, val=0x%08x", __func__, reg, val);
}

/* 配置 PI 边沿 */
static int set_pi_edge(struct pi_dev_t *pdev, u8 edge)
{
    struct pi_ctrl_t *pctrl = (struct pi_ctrl_t *)(pdev->parent);
    u8 edge_code = edge;
    u32 val = 0;

    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&pctrl->lock)) {
        dev_err(pctrl->dev, "Acquire %s %s mutex failed", __func__, pctrl->name);
        return -ERESTARTSYS;
    }
    switch (edge) {
    case PI_EDGE_RISE:
    case PI_EDGE_FALL:
    case PI_EDGE_RISE_FALL:
        break;
    default:
        edge_code = PI_DEF_EDGE;
        dev_warn(pctrl->dev, "PI edge %d not support, use default %d", edge, edge_code);
        break;
    }
    pdev->edge = edge_code;
    pctrl->write_reg(pctrl->pcie_dev, PI_ID_OFFSET, pctrl->id[pdev->index]);
    val = pctrl->read_reg(pctrl->pcie_dev, PI_MODE_OFFSET);
    val &= (~PI_EDGE_MASK);
    val |= ((edge_code << PI_EDGE_SHIFT) & PI_EDGE_MASK);
    pctrl->write_reg(pctrl->pcie_dev, PI_MODE_OFFSET, val);
    dev_dbg(pctrl->dev, "Set PI edge %d success", edge_code);
    /* 释放互斥锁 */
    mutex_unlock(&pctrl->lock);

    return 0;
}

/* 获取 PI 边沿 */
static int get_pi_edge(struct pi_dev_t *pdev)
{
    struct pi_ctrl_t *pctrl = (struct pi_ctrl_t *)(pdev->parent);
    int ret = 0;
    u32 val = 0;

    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&pctrl->lock)) {
        dev_err(pctrl->dev, "Acquire %s %s mutex failed", __func__, pctrl->name);
        return -ERESTARTSYS;
    }
    pctrl->write_reg(pctrl->pcie_dev, PI_ID_OFFSET, pctrl->id[pdev->index]);
    val = pctrl->read_reg(pctrl->pcie_dev, PI_MODE_OFFSET);
    pdev->edge_get = ((val & PI_EDGE_MASK) >> PI_EDGE_SHIFT);
    if (pdev->edge_get != pdev->edge) {
        dev_err(pctrl->dev, "Get PI edge %d, not the set %d", pdev->edge_get, pdev->edge);
        ret = -EFAULT;
        goto unlock;
    } else {
        switch (pdev->edge_get) {
        case PI_EDGE_RISE:
        case PI_EDGE_FALL:
        case PI_EDGE_RISE_FALL:
            dev_dbg(pctrl->dev, "Get PI edge %d, is the set %d", pdev->edge_get, pdev->edge);
            break;
        default:
            dev_err(pctrl->dev, "Get PI edge %d not support", pdev->edge_get);
            break;
        }
    }

unlock:
    /* 释放互斥锁 */
    mutex_unlock(&pctrl->lock);

    return ret;
}

/* 配置 PI 延时时间 */
static int set_pi_delay(struct pi_dev_t *pdev, u16 delay)
{
    struct pi_ctrl_t *pctrl = (struct pi_ctrl_t *)(pdev->parent);
    u32 val = 0;

    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&pctrl->lock)) {
        dev_err(pctrl->dev, "Acquire %s %s mutex failed", __func__, pctrl->name);
        return -ERESTARTSYS;
    }
    pdev->delay = delay;
    pctrl->write_reg(pctrl->pcie_dev, PI_ID_OFFSET, pctrl->id[pdev->index]);
    val = pctrl->read_reg(pctrl->pcie_dev, PI_MODE_OFFSET);
    val &= (~PI_DELAY_MASK);
    val |= ((delay << PI_DELAY_SHIFT) & PI_DELAY_MASK);
    pctrl->write_reg(pctrl->pcie_dev, PI_MODE_OFFSET, val);
    dev_dbg(pctrl->dev, "Set PI delay %dus success", delay);
    /* 释放互斥锁 */
    mutex_unlock(&pctrl->lock);

    return 0;
}

/* 获取 PI 延时时间 */
static int get_pi_delay(struct pi_dev_t *pdev)
{
    struct pi_ctrl_t *pctrl = (struct pi_ctrl_t *)(pdev->parent);
    int ret = 0;
    u32 val = 0;

    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&pctrl->lock)) {
        dev_err(pctrl->dev, "Acquire %s %s mutex failed", __func__, pctrl->name);
        return -ERESTARTSYS;
    }
    pctrl->write_reg(pctrl->pcie_dev, PI_ID_OFFSET, pctrl->id[pdev->index]);
    val = pctrl->read_reg(pctrl->pcie_dev, PI_MODE_OFFSET);
    pdev->delay_get = ((val & PI_DELAY_MASK) >> PI_DELAY_SHIFT);
    if (pdev->delay_get != pdev->delay) {
        dev_err(pctrl->dev, "Get PI delay %dus, not the set %d", pdev->delay_get, pdev->delay);
        ret = -EFAULT;
        goto unlock;
    } else {
        dev_dbg(pctrl->dev, "Get PI delay %dus, is the set %d", pdev->delay_get, pdev->delay);
    }

unlock:
    /* 释放互斥锁 */
    mutex_unlock(&pctrl->lock);

    return ret;
}

/* 获取 PI 周期值 */
static int get_pi_cycle(struct pi_dev_t *pdev)
{
    struct pi_ctrl_t *pctrl = (struct pi_ctrl_t *)(pdev->parent);

    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&pctrl->lock)) {
        dev_err(pctrl->dev, "Acquire %s %s mutex failed", __func__, pctrl->name);
        return -ERESTARTSYS;
    }
    /* To avoid floating point, we multiply constants by PI_FREQ_ZOOM. */
    switch (pctrl->type) {
    case PI_TYPE_AC:
        if (pdev->index == 0) {
            pdev->cycle = pctrl->read_reg(pctrl->pcie_dev, PI_AC_CYCLE1_OFFSET);
            if (pdev->cycle < PI_MAX_CYCLE) {
                pdev->cycle *= PI_CYCLE_REG_LSB;
            }
        } else {
            pdev->cycle = pctrl->read_reg(pctrl->pcie_dev, PI_AC_CYCLE2_OFFSET);
            if (pdev->cycle < PI_MAX_CYCLE) {
                pdev->cycle *= PI_CYCLE_REG_LSB;
            }
        }
        break;
    default:
        pctrl->write_reg(pctrl->pcie_dev, PI_ID_OFFSET, pctrl->id[pdev->index]);
        pdev->cycle = pctrl->read_reg(pctrl->pcie_dev, PI_CYCLE_OFFSET);
        if (pdev->cycle < PI_MAX_CYCLE) {
            pdev->cycle *= PI_CYCLE_REG_LSB;
        }
        break;
    }
    /* 释放互斥锁 */
    mutex_unlock(&pctrl->lock);
    dev_dbg(pctrl->dev, "Get PI%d cycle %uns", pdev->index, pdev->cycle);

    return 0;
}

/* 打开设备 */
static int open_pi(struct inode *inode, struct file *filp)
{
    /* 获取与 inode 关联的字符设备 */
    struct cdev_dev_t *cdev_dev = container_of(inode->i_cdev, struct cdev_dev_t, cdev);
    struct cdev_ctrl_t *cdev_ctrl = (struct cdev_ctrl_t *)(cdev_dev->parent);
    struct pi_ctrl_t *pi_ctrl = (struct pi_ctrl_t *)(cdev_ctrl->parent);
    struct pi_dev_t *priv = &pi_ctrl->pi[cdev_dev->index];

    /* 检查设备是否有效 */
    if (!priv || !priv->dev) {
        pr_err("Invalid parameters for %s", __func__);
        return -ENODEV;
    }
    /* 记录设备指针 */
    filp->private_data = priv;
    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&priv->lock)) {
        dev_err(priv->dev, "Acquire %s mutex failed", cdev_dev->name);
        return -ERESTARTSYS;
    }
    dev_dbg(priv->dev, "Open %s%d device", cdev_ctrl->name, cdev_dev->index);

    mutex_unlock(&priv->lock);

    return 0;
}

/* 关闭设备 */
static int release_pi(struct inode *inode, struct file *filp)
{
    struct pi_dev_t *priv = filp->private_data;

    if (priv) {
        mutex_lock(&priv->lock);
        dev_dbg(priv->dev, "Release %s device", priv->name);
        mutex_unlock(&priv->lock);
    }

    return 0;
}

/* 读取设备 */
static ssize_t read_pi(struct file *filp, char __user *buf, 
                       size_t count, loff_t *ppos)
{
    struct pi_dev_t *priv = filp->private_data;
    ssize_t ret = 0;
    size_t read_len = 0;

    /* 检查参数是否有效 */
    if (!priv || !priv->dev || !buf) {
        pr_err("Invalid parameters for %s", __func__);
        return -ENODEV;
    }

    /* 检查偏移量是否有效 */
    if (*ppos > sizeof(u32)) {
        dev_err(priv->dev, "Beyond buffer size, pos=%lld, size=%zu", 
                *ppos, sizeof(u32));
        return -EFBIG;
    }

    /* 计算实际可读取长度 */
    read_len = min(count, (size_t)(sizeof(u32) - *ppos));

    /* 获取 PI 周期值 */
    get_pi_cycle(priv);
    if (copy_to_user(buf, &priv->cycle, sizeof(priv->cycle))) {
        dev_err(priv->dev, "Copy %s %zu data to user space failed", priv->name, read_len);
        goto unlock;
    }
    dev_dbg(priv->dev, "Read %s %zu data from offset %lld", 
            priv->name, read_len, *ppos);
    /* 更新文件位置 */
    // *ppos += read_len;
    ret = read_len;

unlock:
    mutex_unlock(&priv->lock);

    return ret;
}

/* IOCTL 处理 */
static long ioctl_pi(struct file *filp, unsigned int cmd, 
                          unsigned long arg)
{
    struct pi_dev_t *priv = filp->private_data;
    struct pi_ctrl_t *pctrl = (struct pi_ctrl_t *)(priv->parent);
    long ret = 0;
    u8 u8_val = 0;
    u16 u16_val = 0;

    /* 检查参数是否有效 */
    if (!priv || !priv->dev) {
        pr_err("Invalid parameters for %s", __func__);
        return -ENODEV;
    }

    /* 获取互斥锁 */
    if (mutex_lock_interruptible(&priv->lock)) {
        dev_err(priv->dev, "Acquire %s mutex failed", priv->name);
        return -ERESTARTSYS;
    }
    switch (cmd) {
    /* 配置 PI 边沿 */
    case IOCTL_PI_SET_EDGE:
        if (copy_from_user(&u8_val, (void __user *)arg, sizeof(u8_val))) {
            dev_err(priv->dev, "Set PI edge failed");
            ret = -EFAULT;
            goto unlock;
        }
        if (set_pi_edge(priv, u8_val) < 0) {
            dev_err(priv->dev, "Set PI edge %d failed", u8_val);
            ret = -EFAULT;
            goto unlock;
        }
        break;
    /* 获取 PI 边沿 */
    case IOCTL_PI_GET_EDGE:
        if (get_pi_edge(priv) < 0) {
            dev_err(priv->dev, "Get PI edge %d failed", priv->edge_get);
            ret = -EFAULT;
            goto unlock;
        }
        if (copy_to_user((void __user *)arg, &priv->edge_get, sizeof(priv->edge_get))) {
            dev_err(priv->dev, "Get PI edge %d copy failed", priv->edge_get);
            ret = -EFAULT;
            goto unlock;
        }
        break;
    /* 配置 PI 延时时间 */
    case IOCTL_PI_SET_FILTER:
        if (copy_from_user(&u16_val, (void __user *)arg, sizeof(u16_val))) {
            dev_err(priv->dev, "Set PI delay failed");
            ret = -EFAULT;
            goto unlock;
        }
        if (set_pi_delay(priv, u16_val) < 0) {
            dev_err(priv->dev, "Set PI delay %dus failed", u16_val);
            ret = -EFAULT;
            goto unlock;
        }
        break;
    /* 获取 PI 延时时间 */
    case IOCTL_PI_CHK_FILTER:
        if (get_pi_delay(priv) < 0) {
            dev_err(priv->dev, "Get PI delay %d failed", priv->delay_get);
            ret = -EFAULT;
            goto unlock;
        }
        if (copy_to_user((void __user *)arg, &priv->delay_get, sizeof(priv->delay_get))) {
            dev_err(priv->dev, "Get PI delay %d copy failed", priv->delay_get);
            ret = -EFAULT;
            goto unlock;
        }
        break;
    /* 获取驱动版本号 */
    case IOCTL_PI_GET_DRV_VERSION:
        if (copy_to_user((void __user *)arg, &pctrl->drv_ver, sizeof(pctrl->drv_ver))) {
            dev_err(pctrl->dev, "Get PI driver version %d copy failed", pctrl->drv_ver);
            ret = -EFAULT;
            goto unlock;
        }
        break;
    /* 获取 FPGA 版本号 */
    case IOCTL_PI_GET_FPGA_VERSION:
        if (copy_to_user((void __user *)arg, &pctrl->fpga_ver, sizeof(pctrl->fpga_ver))) {
            dev_err(pctrl->dev, "Get PI FPGA version %d copy failed", pctrl->fpga_ver);
            ret = -EFAULT;
            goto unlock;
        }
        break;
    default:
        dev_err(priv->dev, "Ioctl PI%d cmd=0x%08x, dir=0x%X, size=%u, type=0x%02X('%c'), nr=%u not support\n",
                priv->index, cmd, _IOC_DIR(cmd), _IOC_SIZE(cmd), _IOC_TYPE(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd));
        ret = -ENOTTY;
        break;
    }

unlock:
    mutex_unlock(&priv->lock);

    return ret;
}

/* 初始化 PI 设备 */
static int init_pi_dev(void *parent, struct device *dev, const char *name, 
                       struct pi_dev_t *pi, u8 index)
{
    int ret = 0;

    pi->parent = parent;
    pi->dev = dev;
    /* 初始化互斥锁 */
    mutex_init(&pi->lock);
    strscpy(pi->name, name, sizeof(pi->name));
    pi->index = index;
    pi->cycle = 0;

    return ret;
}

/* 初始化 PI 控制器 */
int init_pi_ctrl(struct pcie_dev_t *pcie_dev, struct cdev_dev_t *cdev_dev, 
                 struct pi_ctrl_t *pctrl, struct pi_dev_t *pi, 
                 const char *ctrl_name, u8 dev_num, u8 type)
{
    int ret = 0;
    int i = 0;
    char name[32];

    pctrl->dev = &pcie_dev->pdev->dev;
    /* 初始化互斥锁 */
    mutex_init(&pctrl->lock);
    snprintf(pctrl->name, sizeof(pctrl->name), "%s", ctrl_name);
    pctrl->num = dev_num;
    pctrl->pcie_dev = pcie_dev;
    pctrl->read_reg = read_pi_reg_le;
    pctrl->write_reg = write_pi_reg_le;
    pctrl->cdev_ctrl.cdev_dev = cdev_dev;
    pctrl->pi = pi;
    pctrl->type = type;
    switch (type) {
    case PI_TYPE_AC:
        pctrl->id[0] = PI_AC_CH0;
        pctrl->id[1] = PI_AC_CH1;
        break;
    default:
        for (i = 0; i < dev_num; i++) {
            pctrl->id[i] = i;
        }
        break;
    }
    pctrl->drv_ver = CDEV_DRV_VERSION(PI_VER_MAJOR, PI_VER_MINOR, PI_VER_PATCH);
    pctrl->fpga_ver = pcie_dev->read_mem(pcie_dev->bar[0] + PI_VERSION_OFFSET);

    /* 初始化字符设备 */
    ret = init_cdev_ctrl(pctrl, &pctrl->cdev_ctrl, pctrl->name, dev_num);
    if (ret < 0) {
        dev_err(pctrl->dev, "Init %s controller failed", pctrl->name);
        goto err_free_dev;
    }
    for (i = 0; i < dev_num; i++) {
        snprintf(name, sizeof(name), "%s%d", pctrl->name, i);
        ret = init_cdev_dev(&pctrl->cdev_ctrl, pctrl->cdev_ctrl.cclass, 
                            &pctrl->cdev_ctrl.cdev_dev[i], &cdev_fops, pctrl->cdev_ctrl.major_devno, name, i);
        if (ret) {
            dev_err(pctrl->dev, "Init %s device failed", name);
            goto err_cleanup_cdev_ctrl;
        }
    }

    /* 初始化 PI 设备*/
    for (i = 0; i < dev_num; i++) {
        init_pi_dev(pctrl, pctrl->dev, pctrl->cdev_ctrl.cdev_dev[i].name, 
                    &pctrl->pi[i], i);
    }

    return 0;

err_cleanup_cdev_ctrl:
    exit_cdev_ctrl(&pctrl->cdev_ctrl);
err_free_dev:

    return ret;
}

/* 注销 PI 控制器 */
void exit_pi_ctrl(struct pi_ctrl_t *pctrl, u8 dev_num)
{
    u8 i = 0;

    if (!pctrl) {
        pr_err("Invalid parameters for %s", __func__);
        return;
    }

    for (i = 0; i < dev_num; i++) {
        exit_cdev_dev(pctrl->cdev_ctrl.cclass, &pctrl->cdev_ctrl.cdev_dev[i]);
    }
    exit_cdev_ctrl(&pctrl->cdev_ctrl);
    dev_info(pctrl->dev, "Release cdev controller success");

    for (i = 0; i < dev_num; i++) {
        mutex_destroy(&pctrl->pi[i].lock);
    }
    /* 注销互斥锁 */
    mutex_destroy(&pctrl->lock);

    dev_info(pctrl->dev, "PCIe character device %s removed", pctrl->name);
}
